package com.hzgj.bcl.soa.api;

import com.hzgj.bcl.concurrent.FutureListener;
import com.hzgj.bcl.soa.config.MethodDef;
import com.hzgj.bcl.soa.config.ReferenceDef;
import com.hzgj.bcl.soa.rpc.RpcDispatcher;
import com.hzgj.bcl.soa.rpc.client.RequestOptions;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import lombok.Setter;

public class ObjectProxy<T> implements InvocationHandler {

    private RpcDispatcher rpcDispatcher;
    @Setter
    private ReferenceDef referenceDef;
    @Setter
    private FutureListener<?> futureListener;

    public ObjectProxy rpcDispatcher(RpcDispatcher rpcDispatcher) {
        this.rpcDispatcher = rpcDispatcher;
        return this;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        if (Object.class == method.getDeclaringClass()) {
            String name = method.getName();
            if ("equals".equals(name)) {
                return proxy == args[0];
            } else if ("hashCode".equals(name)) {
                return System.identityHashCode(proxy);
            } else if ("toString".equals(name)) {
                return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy)) + ", with InvocationHandler " + this;
            } else {
                throw new IllegalStateException(String.valueOf(method));
            }
        }

        StackTraceElement[] temp = Thread.currentThread().getStackTrace();
        StackTraceElement element = temp[3];

        MethodDef methodDef = null;
        if (referenceDef.getMethodDefs() != null) {
            methodDef = referenceDef.getMethodDefs().get(method.getName());
        }

        if (!referenceDef.isAsync()) {
            return rpcDispatcher.callServiceMethod(method, args, new
                    RequestOptions().begin(start)
                    .referenceDef(referenceDef)
                    .methodDef(methodDef)
                    .source(element.getClassName() + "_" + element.getMethodName()));
        }

        if (futureListener == null) {
            throw new RuntimeException("async request must be set futureListener");
        }
        rpcDispatcher.callServiceMethodWithFuture(method, args, new
                RequestOptions()
                .referenceDef(referenceDef)
                .methodDef(methodDef)
                .source(element.getClassName() + "_" + element.getMethodName()), futureListener);
        return null;
    }
}
